Implement cargo-package
authorAlex Crichton <alex@alexcrichton.com>
Fri, 18 Jul 2014 15:40:45 +0000 (08:40 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 27 Aug 2014 02:03:12 +0000 (19:03 -0700)
This command will assemble the current package into a tarball ready for
uploading to the cargo registry. Currently no further verification is done
beyond packaging the local repository into a tarball, but in the future this
could execute other operations such as api stability tools.

Cargo.lock
Cargo.toml
src/bin/cargo.rs
src/bin/package.rs [new file with mode: 0644]
src/cargo/core/package_id.rs
src/cargo/lib.rs
src/cargo/ops/cargo_package.rs [new file with mode: 0644]
src/cargo/ops/mod.rs
tests/support/mod.rs
tests/test_cargo_package.rs [new file with mode: 0644]
tests/tests.rs

index 6b02ba227f72b10df17cb8cedba4eaab1327a3ad..4f5ea5f2420c4415724876424b9a3886bb047923 100644 (file)
@@ -4,8 +4,10 @@ version = "0.0.1-pre"
 dependencies = [
  "docopt 0.6.0 (git+https://github.com/burntsushi/docopt.rs#fc7ba2f1a5a351f7874257d880223d2ff5c75d36)",
  "docopt_macros 0.6.0 (git+https://github.com/burntsushi/docopt.rs#fc7ba2f1a5a351f7874257d880223d2ff5c75d36)",
+ "flate2 0.0.1 (git+https://github.com/alexcrichton/flate2-rs#12593d1b9ccf09c2eabac176a6e233b171eed843)",
  "hamcrest 0.1.0 (git+https://github.com/carllerche/hamcrest-rust.git#f0fd1546b0a7a278a12658ab8602b5c827cc3a42)",
  "semver 0.0.1 (git+https://github.com/rust-lang/semver#c78b40d7fdf8acd99b503e6ce394fbcf9eb8982f)",
+ "tar 0.0.1 (git+https://github.com/alexcrichton/tar-rs#689bbc003ae47feae5bc99c53b56736e4ad994ba)",
  "toml 0.1.0 (git+https://github.com/alexcrichton/toml-rs#8c128cb550ba66d4962cd81acca93857c605eaa0)",
  "url 0.1.0 (git+https://github.com/servo/rust-url#23fb9ec22cca9d643ad2cce9894a947c005b7fe2)",
 ]
@@ -28,6 +30,11 @@ name = "encoding"
 version = "0.1.0"
 source = "git+https://github.com/lifthrasiir/rust-encoding#7e7950ddbd46428a439db3e2594fa78f0972ef2e"
 
+[[package]]
+name = "flate2"
+version = "0.0.1"
+source = "git+https://github.com/alexcrichton/flate2-rs#12593d1b9ccf09c2eabac176a6e233b171eed843"
+
 [[package]]
 name = "hamcrest"
 version = "0.1.0"
@@ -38,6 +45,11 @@ name = "semver"
 version = "0.0.1"
 source = "git+https://github.com/rust-lang/semver#c78b40d7fdf8acd99b503e6ce394fbcf9eb8982f"
 
+[[package]]
+name = "tar"
+version = "0.0.1"
+source = "git+https://github.com/alexcrichton/tar-rs#689bbc003ae47feae5bc99c53b56736e4ad994ba"
+
 [[package]]
 name = "toml"
 version = "0.1.0"
index 4df2ec39c12e1dbcc02befe8dd25eb4ee1d3df9d..e393cb5c963896a9f2bbb6bcac5cfab9d333318b 100644 (file)
@@ -26,6 +26,12 @@ git = "https://github.com/servo/rust-url"
 [dependencies.semver]
 git = "https://github.com/rust-lang/semver"
 
+[dependencies.tar]
+git = "https://github.com/alexcrichton/tar-rs"
+
+[dependencies.flate2]
+git = "https://github.com/alexcrichton/flate2-rs"
+
 [[bin]]
 name = "cargo"
 test = false
index d92837e40a7ce527456895251d096a56d8fe875f..140f2d26da8d670c3e93732b2022b1eac65977b7 100644 (file)
@@ -59,6 +59,7 @@ macro_rules! each_subcommand( ($macro:ident) => ({
     $macro!(git_checkout)
     $macro!(locate_project)
     $macro!(new)
+    $macro!(package)
     $macro!(read_manifest)
     $macro!(run)
     $macro!(test)
diff --git a/src/bin/package.rs b/src/bin/package.rs
new file mode 100644 (file)
index 0000000..18af638
--- /dev/null
@@ -0,0 +1,31 @@
+use docopt;
+use cargo::ops;
+use cargo::core::{MultiShell};
+use cargo::util::{CliResult, CliError};
+use cargo::util::important_paths::find_root_manifest_for_cwd;
+
+docopt!(Options, "
+Assemble a the local package into a distributable tarball
+
+Usage:
+    cargo package [options]
+
+Options:
+    -h, --help              Print this message
+    --manifest-path PATH    Path to the manifest to compile
+    -v, --verbose           Use verbose output
+
+", flag_manifest_path: Option<String>)
+
+pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
+    shell.set_verbose(options.flag_verbose);
+    let Options {
+        flag_manifest_path,
+        ..
+    } = options;
+
+    let root = try!(find_root_manifest_for_cwd(flag_manifest_path.clone()));
+    ops::package(&root, shell).map(|_| None).map_err(|err| {
+        CliError::from_boxed(err, 101)
+    })
+}
index 6f70ad35349a61870d95b5fc96c090962436f130..8070d7c3aeb187b05acd7634541c502dd0527af9 100644 (file)
@@ -1,7 +1,7 @@
 use semver;
 use std::hash::Hash;
 use std::fmt::{mod, Show, Formatter};
-use collections::hash;
+use std::hash;
 use serialize::{
     Encodable,
     Encoder,
index db4e6912e64b5772f76edeccf6c8297d1eac0f90..ff95e2d878a888b46dba24e831e521622422b87d 100644 (file)
@@ -5,19 +5,19 @@
 #![feature(default_type_params)]
 #![deny(warnings)]
 
-extern crate collections;
-extern crate debug;
 extern crate glob;
 extern crate regex;
-extern crate semver;
 extern crate serialize;
 extern crate term;
 extern crate time;
-extern crate url;
 #[phase(plugin)] extern crate regex_macros;
 #[phase(plugin, link)] extern crate log;
 
+extern crate semver;
 extern crate docopt;
+extern crate flate2;
+extern crate tar;
+extern crate url;
 extern crate toml;
 #[cfg(test)] extern crate hamcrest;
 
diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs
new file mode 100644 (file)
index 0000000..b5c7187
--- /dev/null
@@ -0,0 +1,60 @@
+use std::io::File;
+
+use tar::Archive;
+use flate2::{GzBuilder, BestCompression};
+
+use core::source::Source;
+use core::{Package, MultiShell};
+use sources::PathSource;
+use util::{CargoResult, human, internal, ChainError, Require};
+
+pub fn package(manifest_path: &Path,
+               shell: &mut MultiShell) -> CargoResult<Path> {
+    let mut src = try!(PathSource::for_path(&manifest_path.dir_path()));
+    try!(src.update());
+    let pkg = try!(src.get_root_package());
+
+    let filename = format!("{}-{}.tar.gz", pkg.get_name(), pkg.get_version());
+    let dst = pkg.get_manifest_path().dir_path().join(filename);
+    try!(shell.status("Packaging", pkg.get_package_id().to_string()));
+    try!(tar(&pkg, &src, shell, &dst).chain_error(|| {
+        human("failed to prepare local package for uploading")
+    }));
+
+    Ok(dst)
+}
+
+fn tar(pkg: &Package, src: &PathSource, shell: &mut MultiShell,
+       dst: &Path) -> CargoResult<()> {
+
+    if dst.exists() {
+        return Err(human(format!("destination already exists: {}",
+                                 dst.display())))
+    }
+    let tmpfile = try!(File::create(dst));
+
+    // Prepare the encoder and its header
+    let encoder = GzBuilder::new().filename(dst.filename().unwrap())
+                                  .writer(tmpfile, BestCompression);
+
+    // Put all package files into a compressed archive
+    let ar = Archive::new(encoder);
+    for file in try!(src.list_files(pkg)).iter() {
+        let relative = file.path_relative_from(&dst.dir_path()).unwrap();
+        let relative = try!(relative.as_str().require(|| {
+            human(format!("non-utf8 path in source directory: {}",
+                          relative.display()))
+        }));
+        let mut file = try!(File::open(file));
+        try!(shell.verbose(|shell| {
+            shell.status("Archiving", relative.as_slice())
+        }));
+        let path = format!("{}-{}/{}", pkg.get_name(),
+                           pkg.get_version(), relative);
+        try!(ar.append(path.as_slice(), &mut file).chain_error(|| {
+            internal(format!("could not archive source file `{}`", relative))
+        }));
+    }
+
+    Ok(())
+}
index 71846ecf6dcb2dcafb0d974c382c9c7441c021a7..3635857c73fdc6ccf69c31036f2a42bb5d29a963 100644 (file)
@@ -8,6 +8,7 @@ pub use self::cargo_doc::{doc, DocOptions};
 pub use self::cargo_generate_lockfile::{generate_lockfile, write_resolve};
 pub use self::cargo_generate_lockfile::{update_lockfile, load_lockfile};
 pub use self::cargo_test::{run_tests, run_benches, TestOptions};
+pub use self::cargo_package::package;
 
 mod cargo_clean;
 mod cargo_compile;
@@ -18,3 +19,4 @@ mod cargo_new;
 mod cargo_doc;
 mod cargo_generate_lockfile;
 mod cargo_test;
+mod cargo_package;
index c480b6f4a3d6517225d90972e9e2728f4858f4f3..08e42c9356f52bb878cffecb535355c8e27ca978 100644 (file)
@@ -507,3 +507,4 @@ pub static COMPILING: &'static str = "   Compiling";
 pub static FRESH:     &'static str = "       Fresh";
 pub static UPDATING:  &'static str = "    Updating";
 pub static DOCTEST:   &'static str = "   Doc-tests";
+pub static PACKAGING: &'static str = "   Packaging";
diff --git a/tests/test_cargo_package.rs b/tests/test_cargo_package.rs
new file mode 100644 (file)
index 0000000..969a2e6
--- /dev/null
@@ -0,0 +1,27 @@
+use support::{project, execs};
+use support::{PACKAGING};
+use hamcrest::{assert_that, existing_file};
+
+fn setup() {
+}
+
+test!(simple {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("src/main.rs", r#"
+            fn main() { println!("hello"); }
+        "#);
+
+    assert_that(p.cargo_process("package"),
+                execs().with_status(0).with_stdout(format!("\
+{packaging} foo v0.0.1 ({dir})
+",
+        packaging = PACKAGING,
+        dir = p.url()).as_slice()));
+    assert_that(&p.root().join("foo-0.0.1.tar.gz"), existing_file());
+})
index 966e572aa9f9084dc3b5e9e99f13a23fa7739c0d..c84e206338be92c71f7003b1bdaeec93b5f7904e 100644 (file)
@@ -37,3 +37,4 @@ mod test_cargo_compile_plugins;
 mod test_cargo_doc;
 mod test_cargo_freshness;
 mod test_cargo_generate_lockfile;
+mod test_cargo_package;